home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.Lib / CtlHandler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  30.0 KB  |  1,028 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:        DTS.Lib
  5. ** File:        ctlhandler.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1991-1993 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* You may incorporate this sample code into your applications without
  13. ** restriction, though the sample code has been provided "AS IS" and the
  14. ** responsibility for its operation is 100% yours.  However, what you are
  15. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  16. ** after having made changes. If you're going to re-distribute the source,
  17. ** we require that you make it clear in the source that the code was
  18. ** descended from Apple Sample Code, but that you've made changes. */
  19.  
  20. /* This code implements the new 7.0 human-interface standards for both
  21. ** TextEdit and List controls.  These standards include the following features:
  22. **
  23. ** 1) Tabbing between TextEdit and List controls within a window.
  24. ** 2) Displaying what item is active.  The active TextEdit item is indicated
  25. **    by either a blinking caret, or a selection range.
  26. ** 3) List positioning via the keyboard.  Entries on the keyboard automatically
  27. **    select and display the closest List item.  Also, the up and down arrows
  28. **    scroll through the list.
  29. ** 4) Window scrollbars are handled.
  30. */
  31.  
  32.  
  33.  
  34. /*****************************************************************************/
  35.  
  36.  
  37.  
  38. #include "DTS.Lib2.h"
  39. #include "DTS.Lib.protos.h"
  40.  
  41. #include "ListControlProcs.h"
  42. #include "TextEditControlProcs.h"
  43.  
  44. #ifndef __STDDEF__
  45. #include <StdDef.h>
  46. #endif
  47.  
  48. static short    HandleScrollEvent(WindowPtr window, EventRecord *event);
  49.  
  50.  
  51.  
  52. /*****************************************************************************/
  53.  
  54.  
  55.  
  56. extern short        gBeginUpdateNested;
  57.  
  58. short                gDataCtl = rDataCtl;
  59.  
  60. static FileRecHndl    gFrHndl;
  61. static Rect            gScrollRct;
  62. static Point        gKeepOrg;
  63. static Boolean        gVert;
  64.  
  65. static pascal void    ScrollActionProc(ControlHandle scrollCtl, short part);
  66. static void            ScrollTheContent(WindowPtr window, short h, short v);
  67. static short        GetCtlPosition(ControlHandle ctl);
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72. /*****************************************************************************/
  73. /*****************************************************************************/
  74.  
  75.  
  76.  
  77. /* This function converts a control number to a control handle.  The function
  78. ** simply walks the window's control list counting down until it has reached
  79. ** the right control number.  It also returns the number of controls traversed.
  80. ** While often this will be the same as the control number passed in, if the
  81. ** number passed in is greater than the number of controls in the list, then
  82. ** the number returned is the number of controls in the list. */
  83.  
  84. /* NOTE: Additional support for control id's has been added.  To store the control id (cid),
  85. ** the control handle is grown, and the cid is stored after the control title.  This means
  86. ** that you can't directly call SetCTitle without losing the cid, as SetCTitle resizes the
  87. ** control handle.  You should instead call SetStyledCTitle, which preserves the cid.
  88. ** For backwards compatibility, CNum2Ctl and Ctl2CNum support both the old-style control
  89. ** numbers and the new-style control id's.  While backwards compatibility may be a good
  90. ** thing, what this means is that you have to assign your cid's so that the value is greater
  91. ** than the number of controls in the window. */
  92.  
  93. #pragma segment Controls
  94. short    CNum2Ctl(WindowPtr window, short ctlNum, ControlHandle *ctl)
  95. {
  96.     short    numCtls, cid;
  97.  
  98.     *ctl = nil;
  99.     if (!ctlNum) return(0);
  100.  
  101.     cid = ctlNum;    /* Caller may have actually passed us a cid. */
  102.  
  103.     *ctl = ((WindowPeek)window)->controlList;
  104.     for (numCtls = 1; --ctlNum;  ++numCtls) {
  105.         if (!*ctl) {
  106.             --numCtls;
  107.             break;
  108.         }
  109.         if (GetControlID(*ctl) == cid) break;
  110.         *ctl = (**ctl)->nextControl;
  111.     }
  112.  
  113.     return(numCtls);
  114. }
  115.  
  116.  
  117.  
  118. /*****************************************************************************/
  119.  
  120.  
  121.  
  122. /* This function converts a control handle to a control number.  The function
  123. ** simply walks the window's control list and counts how many controls it
  124. ** has to traverse before finding the target control.  The smallest control
  125. ** number that can be returned is 1.  This makes control numbers similar to
  126. ** dialog item numbers. */
  127.  
  128. /* NOTE: Additional support for control id's has been added.  To store the control id (cid),
  129. ** the control handle is grown, and the cid is stored after the control title.  This means
  130. ** that you can't directly call SetCTitle without losing the cid, as SetCTitle resizes the
  131. ** control handle.  You should instead call SetStyledCTitle, which preserves the cid.
  132. ** For backwards compatibility, CNum2Ctl and Ctl2CNum support both the old-style control
  133. ** numbers and the new-style control id's.  While backwards compatibility may be a good
  134. ** thing, what this means is that you have to assign your cid's so that the value is greater
  135. ** than the number of controls in the window. */
  136.  
  137. #pragma segment Controls
  138. short    Ctl2CNum(ControlHandle ctl)
  139. {
  140.     ControlHandle    nextCtl;
  141.     short            ctlNum;
  142.  
  143.     if (!ctl)                 return(0);
  144.     if (ctlNum = GetControlID(ctl)) return(ctlNum);
  145.  
  146.     nextCtl = ((WindowPeek)(*ctl)->contrlOwner)->controlList;
  147.     for (ctlNum = 0;;) {
  148.         if (!nextCtl) break;
  149.         ++ctlNum;
  150.         if (ctl == nextCtl) break;
  151.         nextCtl = (*nextCtl)->nextControl;
  152.     }
  153.     return(ctlNum);
  154. }
  155.  
  156.  
  157.  
  158. /*****************************************************************************/
  159.  
  160.  
  161.  
  162. /* This function reactivates the last active TextEdit or List control for
  163. ** the indicated window. */
  164.  
  165. #pragma segment Controls
  166. void    DoCtlActivate(WindowPtr window)
  167. {
  168.     ListHandle    list;
  169.     TEHandle    te, oldte;
  170.     WindowPtr    oldww;
  171.  
  172.     if (list = (*gclWindActivate)(window, false)) {
  173.         if ((*list)->rView.left < -8192)
  174.             BeginFrame(window);
  175.         else
  176.             BeginContent(window);
  177.         (*gclWindActivate)(window, true);        /* Reactivate the list. */
  178.         EndContent(window);                        /* Same as EndFrame().  */
  179.     }
  180.     else {
  181.         oldte = (*gcteFindActive)(nil);
  182.         te    = (*gcteWindActivate)(window, false);
  183.         if (te != oldte) {
  184.             if (oldte) {
  185.                 oldww = (*oldte)->inPort;
  186.                 if ((*oldte)->viewRect.left < -8192)
  187.                     BeginFrame(oldww);
  188.                 else
  189.                     BeginContent(oldww);
  190.                 (*gcteWindActivate)(oldww, true);
  191.                 EndContent(oldww);
  192.             }
  193.             if (te) {
  194.                 if ((*te)->viewRect.left < -8192)
  195.                     BeginFrame(window);
  196.                 else
  197.                     BeginContent(window);
  198.                 (*gcteWindActivate)(window, true);
  199.                 EndContent(window);
  200.             }
  201.         }
  202.     }
  203. }
  204.  
  205.  
  206.  
  207. /*****************************************************************************/
  208.  
  209.  
  210.  
  211. /* This function returns all the checkBox values into the dsignated array.
  212. ** The function walks the control list, and when it finds a checkBox control,
  213. ** it gets the control value and places it in the next position in the array.
  214. ** This allows a single call to retrieve all the checkBox values at once. */
  215.  
  216. #pragma segment Controls
  217. void    GetCheckBoxValues(WindowPtr window, Boolean checkBoxVal[])
  218. {
  219.     ControlHandle    nextCtl;
  220.     short            checkBoxIndx;
  221.  
  222.     nextCtl = ((WindowPeek)window)->controlList;
  223.     for (checkBoxIndx = 0;;) {
  224.         if (!nextCtl) return;
  225.         if (GetButtonVariant(nextCtl) == checkBoxProc)
  226.             checkBoxVal[checkBoxIndx++] = GetCtlValue(nextCtl);
  227.         nextCtl = (*nextCtl)->nextControl;
  228.     }
  229. }
  230.  
  231.  
  232.  
  233. /*****************************************************************************/
  234.  
  235.  
  236.  
  237. /* This function returns which radio button is selected for a particular
  238. ** family of radio buttons.  It finds the radio button of the target family
  239. ** with the lowest control number and it subtracts this number from the
  240. ** selected radio button of the same family.  This gives a relative radio
  241. ** button number as a return result.  This way the position of the family
  242. ** of radio buttons can change in the window and the return result is the
  243. ** same.  The family number is stored in the control's refCon field. */
  244.  
  245. #pragma segment Controls
  246. short    GetRadioButtonChoice(WindowPtr window, short famNum)
  247. {
  248.     ControlHandle    nextCtl;
  249.     short            ctlNum, firstInFam;
  250.  
  251.     nextCtl = ((WindowPeek)window)->controlList;
  252.     for (ctlNum = 0, firstInFam = -1;;) {
  253.         if (!nextCtl) return(-1);        /* If a proper radio button family was
  254.                                         ** passed in, this can't happen.  The
  255.                                         ** -1 is an error that indicates that
  256.                                         ** the requested family didn't exist,
  257.                                         ** or that there was no selected
  258.                                         ** radio of the requested family. */
  259.         ++ctlNum;
  260.         if (GetButtonVariant(nextCtl) == radioButProc) {
  261.             if (GetCRefCon(nextCtl) == famNum) {
  262.                 if (firstInFam == -1)
  263.                     firstInFam = ctlNum;
  264.                 if (GetCtlValue(nextCtl)) return(ctlNum - firstInFam);
  265.             }
  266.         }
  267.         nextCtl = (*nextCtl)->nextControl;
  268.     }
  269. }
  270.  
  271.  
  272.  
  273. /*****************************************************************************/
  274.  
  275.  
  276.  
  277. /* This function currently handles events for TextEdit, List, and button
  278. ** controls in a window.  It also handles the window scrollbars and
  279. ** scrolling of the window. */
  280.  
  281. #pragma segment Controls
  282. short    IsCtlEvent(WindowPtr window, EventRecord *event, ControlHandle *retCtl, short *retAction)
  283. {
  284.     RgnHandle            docScroller, docFrame;
  285.     TEHandle            te, oldte, teNext;
  286.     ListHandle            list, oldlist, listNext;
  287.     ControlHandle        xxx, activeCtl, teCtl, listCtl, nextCtl, newCtl, dataCtl;
  288.     short                ctlNum, key, modifiers, mode, action, dir, pass, visMode;
  289.     short                thisNum, teNum, listNum, i, oldVal, newVal, dpass, part;
  290.     Boolean                hitFrame, hitScrollBar, tracked, hasBalloon;
  291.     CTEDataHndl            teData;
  292.     CLDataHndl            listData;
  293.     EventRecord            evt;
  294.     WindowPtr            ww;
  295.     ControlStyleInfo    cinfo;
  296.     short                targetChr, targetMod;
  297.     Str255                dataCtlTitle;
  298.     OSType                sftype;
  299.     Rect                org;
  300.     Point                mouseLoc;
  301.  
  302.     pass = 0;
  303.  
  304.     if (!retCtl)    retCtl    = &xxx;
  305.     if (!retAction) retAction = &action;
  306.  
  307.     *retCtl    = nil;
  308.     *retAction = 0;
  309.  
  310.     evt = *event;
  311.  
  312.     if (evt.what == nullEvent) {
  313.         mouseLoc = GetGlobalMouse();
  314.         window = FrontWindowOfType(kwIsModalDialog, true);
  315.         if (!window) {
  316.             part = FindWindow(mouseLoc, &window);
  317.             if (part == inContent)
  318.                 if (!(((WindowPeek)window)->hilited))
  319.                     window = nil;
  320.         }
  321.         hasBalloon = false;
  322.         if (window) {
  323.             org = window->portRect;
  324.             BeginContent(window);
  325.             hasBalloon = ControlBalloonHelp(window, mouseLoc);
  326.             SetOrigin(org.left, org.top);
  327.             EndContent(window);
  328.             if (hasBalloon) return(0);
  329.             BeginFrame(window);
  330.             hasBalloon = ControlBalloonHelp(window, mouseLoc);
  331.             SetOrigin(org.left, org.top);
  332.             EndFrame(window);
  333.         }
  334.         if (!hasBalloon) ControlBalloonHelp(nil, mouseLoc);
  335.         return(0);
  336.     }
  337.  
  338.     if (!window) return(0);
  339.  
  340.     org = window->portRect;
  341.  
  342.     if (evt.what == mouseDown) {
  343.  
  344.         docScroller = DoCalcScrollRgn(window);
  345.         hitFrame    = PtInRgn(evt.where, docScroller);
  346.         DisposeRgn(docScroller);
  347.         if (hitFrame) return(HandleScrollEvent(window, &evt));
  348.             /* If window scrollbar is clicked on, handle the window scroll event. */
  349.  
  350.         docFrame = DoCalcFrameRgn(window);
  351.         hitFrame = PtInRgn(evt.where, docFrame);
  352.         DisposeRgn(docFrame);
  353.  
  354.         evt.where = event->where;
  355.         if (hitFrame)
  356.             BeginFrame(window);
  357.         else
  358.             BeginContent(window);
  359.         GlobalToLocal(&evt.where);
  360.  
  361.         (*gcteCtlHit)();    /* Clear CTECtl defProc's last hit CTECtl. */
  362.         (*gclCtlHit)();        /* Clear CLCtl defProc's last hit CLCtl. */
  363.  
  364.         if (WhichControl(evt.where, 0, window, retCtl)) {
  365.                 /* WhichControl also finds inactive controls.  We need this for scrollbars.
  366.                 ** If an inactive scrollbar is hit, we should activate the related
  367.                 ** TextEdit or List control. */
  368.  
  369.             hitScrollBar = IsScrollBar(*retCtl);
  370.                 /* The List controls and TextEdit controls may have scrollbars.
  371.                 ** Find out if a scrollbar was pressed, because it may belong
  372.                 ** to a TextEdit or List control. */
  373.  
  374.             FindControl(evt.where, window, retCtl);
  375.                 /* WhichControl doesn't call the scrollProc.  FindControl does.
  376.                 ** We need it called so we can determine below if a TextEdit or
  377.                 ** List control was hit.  CTECtlHit() and CLCtlHit() return
  378.                 ** the last hit respective control. */
  379.  
  380.             ctlNum = Ctl2CNum(*retCtl);
  381.  
  382.             if ((hitScrollBar) || ((*gcteCtlHit)())) {
  383.                     /* This test is for speed.  CTEClick would find out if a TextEdit
  384.                     ** control handled the mouse click, but not as fast as we would
  385.                     ** like.  The above test determines if it is worth investigating. */
  386.  
  387.                 if (CTEFindCtl(window, event, &te, nil)) {
  388.                     ww = CTETargetInfo(&oldte, nil);
  389.                     SetOrigin(org.left, org.top);
  390.                     EndContent(window);
  391.                     if ((oldte) && (te != oldte)) {
  392.                         if ((*oldte)->viewRect.left < -8192)
  393.                             BeginFrame(ww);
  394.                         else
  395.                             BeginContent(ww);
  396.                         (*gcteActivate)(false, oldte);
  397.                         SetOrigin(org.left, org.top);
  398.                         EndContent(ww);
  399.                     }
  400.                     if (list = (*gclFindActive)(window)) {
  401.                         if ((*list)->rView.left < -8192)
  402.                             BeginFrame(window);
  403.                         else
  404.                             BeginContent(window);
  405.                         (*gclActivate)(false, list);
  406.                         SetOrigin(org.left, org.top);
  407.                         EndContent(window);
  408.                     }
  409.                     if (evt.where.h < -8192)
  410.                         BeginFrame(window);
  411.                     else
  412.                         BeginContent(window);
  413.                     if ((*gcteClick)(window, event, retAction)) {    /* If click for TE control... */
  414.                         if (*retAction == -1) {
  415.                             *retCtl = (*gcteViewFromTE)(CTEFindActive(window));
  416.                             teData  = (CTEDataHndl)(**retCtl)->contrlData;
  417.                             mode    = (*teData)->mode;
  418.                             if (!(mode & cteTwoStep))
  419.                                 (*gcteClick)(window, event, retAction);
  420.                         }
  421.                         SetOrigin(org.left, org.top);
  422.                         EndContent(window);
  423.                         return(ctlNum);
  424.                     }
  425.                 }
  426.             }
  427.  
  428.             if ((hitScrollBar) || ((*gclCtlHit)())) {
  429.                     /* This test is for speed.  CLClick would find out if a List
  430.                     ** control handled the mouse click, but not as fast as we would
  431.                     ** like.  The above test determines if it is worth investigating. */
  432.                 if (CLFindCtl(window, event, &list, nil)) {
  433.                     oldlist = CLFindActive(window);
  434.                     SetOrigin(org.left, org.top);
  435.                     EndContent(window);
  436.                     if ((oldlist) && (list != oldlist)) {
  437.                         if ((*oldlist)->rView.left < -8192)
  438.                             BeginFrame(window);
  439.                         else
  440.                             BeginContent(window);
  441.                         (*gclActivate)(false, oldlist);
  442.                         SetOrigin(org.left, org.top);
  443.                         EndContent(window);
  444.                     }
  445.                     if (te = (*gcteFindActive)(window)) {
  446.                         if ((*te)->viewRect.left < -8192)
  447.                             BeginFrame(window);
  448.                         else
  449.                             BeginContent(window);
  450.                         (*gcteActivate)(false, te);
  451.                         SetOrigin(org.left, org.top);
  452.                         EndContent(window);
  453.                     }
  454.                     if (evt.where.h < -8192)
  455.                         BeginFrame(window);
  456.                     else
  457.                         BeginContent(window);
  458.                     if ((*gclClick)(window, event, retAction)) {    /* If click for List control... */
  459.                         if (*retAction == -1) {        /* Just activated a List control... */
  460.                             *retCtl  = (*gclViewFromList)((*gclFindActive)(window));
  461.                             listData = (CLDataHndl)(**retCtl)->contrlData;
  462.                             mode     = (*listData)->mode;
  463.                             if (!(mode & clTwoStep))
  464.                                 (*gclClick)(window, event, retAction);
  465.                         }
  466.                         SetOrigin(org.left, org.top);
  467.                         EndContent(window);
  468.                         return(ctlNum);
  469.                     }
  470.                 }
  471.             }
  472.  
  473.             *retAction = 0;
  474.             if (!*retCtl) {
  475.                 SetOrigin(org.left, org.top);
  476.                 EndContent(window);
  477.                 return(ctlNum);
  478.             }
  479.  
  480.             UseControlStyle(*retCtl);
  481.             WhichControl(evt.where, evt.when, window, nil);
  482.                 /* WhichControl places the hit control in the global gWhichCtl.
  483.                 ** This global is used by CCIconCtl to determine double-clicks.
  484.                 ** Double-clicking is also calculated by WhichControl.  The global
  485.                 ** gWhichCtlDbl is set true if the control was double-clicked on.
  486.                 ** We call WhichControl with the click time just prior to tracking
  487.                 ** the control.  When a non-0 tick is passed in, it compares the found
  488.                 ** control against the last found control, and the tick against the last
  489.                 ** tick.  The globals are set accordingly. */
  490.  
  491.             oldVal  = (**retCtl)->contrlValue;
  492.             tracked = TrackControl(*retCtl, evt.where, (ProcPtr)-1);
  493.             newVal  = (**retCtl)->contrlValue;
  494.             UseControlStyle(nil);
  495.  
  496.             if (tracked) {                    /* Handle other controls. */
  497.  
  498.                 switch(GetButtonVariant(*retCtl)) {
  499.                     case pushButProc:
  500.                         break;
  501.                     case checkBoxProc:
  502.                         SetStyledCtlValue(*retCtl, GetCtlValue(*retCtl) ^ 1);
  503.                                 /* Toggle checkBox value. */
  504.                         break;
  505.                     case radioButProc:
  506.                         nextCtl = ((WindowPeek)window)->controlList;
  507.                             /* The below loop walks the control list for the window and
  508.                             ** finds radio buttons of the correct family number.  If
  509.                             ** the found radio button is the one that was clicked on,
  510.                             ** the value is set true, otherwise it is set false. */
  511.                         for (; nextCtl; nextCtl = (*nextCtl)->nextControl) {
  512.                             if (GetButtonVariant(nextCtl) == radioButProc)
  513.                                 if (GetCRefCon(nextCtl) == GetCRefCon(*retCtl))
  514.                                     SetStyledCtlValue(nextCtl, (nextCtl == *retCtl));
  515.                         }
  516.                         break;
  517.                 }
  518.  
  519.                 if (GetControlStyle(*retCtl, &cinfo)) {
  520.                     for (i = 1;; i += 6) {
  521.                         if (i >= cinfo.keyEquivs[0]) break;
  522.                         if (cinfo.keyEquivs[i] == (unsigned char)',') ++i;
  523.                         if (cinfo.keyEquivs[i] == (unsigned char)':') {
  524.                             targetChr = GetHexByte((char *)(cinfo.keyEquivs + i + 1));
  525.                             targetMod = GetHexByte((char *)(cinfo.keyEquivs + i + 2 + 1)) << 8;
  526.                             evt.what       = keyDown;
  527.                             evt.message    = targetChr;
  528.                             evt.modifiers &= 0x00FF;
  529.                             evt.modifiers |= (targetMod << 8);
  530.                             pass = 1;
  531.                             if (cinfo.keyEquivs[0] > i + 4)
  532.                                 if (cinfo.keyEquivs[i + 5] == '<')
  533.                                     pass = 0;
  534.                                         /* If delimiter is a <, then reprocess with
  535.                                         ** pass #0.  This could cause an infinite loop
  536.                                         ** if the targetEquiv is the same as what was
  537.                                         ** pressed.  The reason that this is done is if
  538.                                         ** the targetEquiv is different than the original
  539.                                         ** event, and we want to give non-te, non-list controls
  540.                                         ** a chance to process the equiv.  Normally the whole
  541.                                         ** purpose for this feature is to first notice that a
  542.                                         ** pass-0 control has an equiv, and then we want to pass
  543.                                         ** this key onto a te control. */
  544.                         }
  545.                     }
  546.                 }
  547.  
  548.                 if (oldVal != newVal) {
  549.                     oldVal -= (**retCtl)->contrlMin;
  550.                     newVal -= (**retCtl)->contrlMin;
  551.                     CNum2Ctl(window, -ctlNum, &dataCtl);
  552.                     if (dataCtl) {
  553.                         GetCTitle(dataCtl, dataCtlTitle);
  554.                         switch (GetCVariant(dataCtl)) {
  555.                             case 0:
  556.                                 for (dpass = 0; dpass < 2; ++dpass) {
  557.                                     i  = (dpass) ? newVal : oldVal;
  558.                                     i *= 7;
  559.                                     i += 2;
  560.                                     if (i <= dataCtlTitle[0] - 3) {
  561.                                         BlockMove(dataCtlTitle + i, &sftype, sizeof(OSType));
  562.                                         visMode = (dpass) ? kwStandardVis : kwHideAll;
  563.                                         DisplayControlSet(window, sftype, visMode);
  564.                                     }
  565.                                 }
  566.                                 break;
  567.                         }
  568.                     }
  569.                 }
  570.             }
  571.             else {
  572.                 *retCtl = nil;
  573.                 ctlNum = 0;
  574.             }
  575.  
  576.             SetOrigin(org.left, org.top);
  577.             EndContent(window);
  578.  
  579.             if (evt.what != keyDown) return(ctlNum);
  580.         }
  581.  
  582.         if (evt.what != keyDown) {
  583.             SetOrigin(org.left, org.top);
  584.             EndContent(window);
  585.             return(0);
  586.         }
  587.     }
  588.  
  589.     if ((evt.what == keyDown) || (evt.what == autoKey)) {        /* If event was keypress... */
  590.  
  591.         modifiers = evt.modifiers;
  592.         key       = evt.message & charCodeMask;
  593.         dir       = (modifiers & shiftKey) ? -1 : 1;
  594.  
  595.         if (key == chTab) {        /* If tab... */
  596.  
  597.             teNext    = nil;
  598.             listNext  = nil;
  599.             activeCtl = nil;
  600.  
  601.             if (te = (*gcteFindActive)(window))
  602.                 activeCtl = (*gcteViewFromTE)(te);
  603.             if (list = (*gclFindActive)(window))
  604.                 activeCtl = (*gclViewFromList)(list);
  605.                     /* Find what the active control is. */
  606.  
  607.             if (!(teCtl = (*gcteNext)(window, &teNext, activeCtl, dir, true)))
  608.                 teCtl = (*gcteNext)(window, &teNext, nil, dir, true);
  609.                     /* Find the next TextEdit control from the active control. */
  610.  
  611.             if (!(listCtl = (*gclNext)(window, &listNext, activeCtl, dir, true)))
  612.                 listCtl = (*gclNext)(window, &listNext, nil, dir, true);
  613.                     /* Find the next List control from the active control. */
  614.  
  615.             if ((!teNext) && (!listNext)) return(0);
  616.                 /* No TextEdit or List controls in window, so we are done. */
  617.  
  618.             thisNum = GetCtlPosition(activeCtl);
  619.             teNum   = GetCtlPosition(teCtl);
  620.             listNum = GetCtlPosition(listCtl);
  621.  
  622.             newCtl = activeCtl;
  623.             if (dir == 1) {
  624.                 if (!teNum)   teNum   = 32767;
  625.                 if (!listNum) listNum = 32767;
  626.                 if (teNum   <= thisNum) teNum   += 16384;
  627.                 if (listNum <= thisNum) listNum += 16384;
  628.                 if (teNum < listNum) newCtl = teCtl;
  629.                 if (listNum < teNum) newCtl = listCtl;
  630.             }
  631.             else {
  632.                 if (!teNum)   teNum   = -32767;
  633.                 if (!listNum) listNum = -32767;
  634.                 if (teNum   >= thisNum) teNum   -= 16384;
  635.                 if (listNum >= thisNum) listNum -= 16384;
  636.                 if (teNum > listNum) newCtl = teCtl;
  637.                 if (listNum > teNum) newCtl = listCtl;
  638.             }
  639.  
  640.             if (activeCtl == newCtl) return(0);
  641.  
  642.             if (te) {            /* If old ctl is te, deactivate it. */
  643.                 if ((*te)->viewRect.left < -8192)
  644.                     BeginFrame(window);
  645.                 else
  646.                     BeginContent(window);
  647.                 (*gcteActivate)(false, te);
  648.                 SetOrigin(org.left, org.top);
  649.                 EndContent(window);
  650.             }
  651.  
  652.             if (list) {            /* If old ctl is list, deactivate it. */
  653.                 if ((*list)->rView.left < -8192)
  654.                     BeginFrame(window);
  655.                 else
  656.                     BeginContent(window);
  657.                 (*gclActivate)(false, list);
  658.                 SetOrigin(org.left, org.top);
  659.                 EndContent(window);
  660.             }
  661.  
  662.             if (newCtl == teCtl) {
  663.                 if ((*teNext)->viewRect.left < -8192)
  664.                     BeginFrame(window);
  665.                 else
  666.                     BeginContent(window);
  667.                 (*gcteActivate)(true, teNext);
  668.                 teData = (CTEDataHndl)(*teCtl)->contrlData;
  669.                 if ((*teData)->mode & cteTabSelectAll)
  670.                     (*gcteSetSelect)(0, (*teNext)->teLength, teNext);
  671.                         /* If the "select all TextEdit text when tabbed into" bit is
  672.                         ** set, then do that very thing. */
  673.                 SetOrigin(org.left, org.top);
  674.                 EndContent(window);
  675.                 return(Ctl2CNum(*retCtl = teCtl));
  676.             }
  677.  
  678.             if (newCtl == listCtl) {
  679.                 if ((*listNext)->rView.left < -8192)
  680.                     BeginFrame(window);
  681.                 else
  682.                     BeginContent(window);
  683.                 (*gclActivate)(true, listNext);
  684.                 SetOrigin(org.left, org.top);
  685.                 EndContent(window);        /* Remove the clipping. */
  686.                 return(Ctl2CNum(*retCtl = listCtl));
  687.             }
  688.         }
  689.  
  690.         for (; pass < 3; ++pass) {
  691.             switch (pass) {
  692.                 case 0:
  693.                 case 2:
  694.                     if (ControlKeyEquiv(window, &evt, retCtl, ((pass) ? "\p031900,0D1900" :
  695.                                                                         "\p031900"))) {
  696.                         if ((**retCtl)->contrlRect.left < -8192)
  697.                             BeginFrame(window);
  698.                         else
  699.                             BeginContent(window);
  700.                         SelectButton(*retCtl);
  701.                         switch(GetButtonVariant(*retCtl)) {
  702.                             case pushButProc:
  703.                                 break;
  704.                             case checkBoxProc:
  705.                                 SetStyledCtlValue(*retCtl, GetCtlValue(*retCtl) ^ 1);
  706.                                         /* Toggle checkBox value. */
  707.                                 break;
  708.                             case radioButProc:
  709.                                 nextCtl = ((WindowPeek)window)->controlList;
  710.                                     /* The below loop walks the control list for the window and
  711.                                     ** finds radio buttons of the correct family number.  If
  712.                                     ** the found radio button is the one that was clicked on,
  713.                                     ** the value is set true, otherwise it is set false. */
  714.                                 for (; nextCtl; nextCtl = (*nextCtl)->nextControl) {
  715.                                     if (GetButtonVariant(nextCtl) == radioButProc)
  716.                                         if (GetCRefCon(nextCtl) == GetCRefCon(*retCtl))
  717.                                             SetStyledCtlValue(nextCtl, (nextCtl == *retCtl));
  718.                                 }
  719.                                 break;
  720.                         }
  721.                         SetOrigin(org.left, org.top);
  722.                         EndContent(window);
  723.  
  724.                         if (!pass) {
  725.                             if (GetControlStyle(*retCtl, &cinfo)) {
  726.                                 for (i = 1;; i += 6) {
  727.                                     if (i >= cinfo.keyEquivs[0]) break;
  728.                                     if (cinfo.keyEquivs[i] == (unsigned char)',') ++i;
  729.                                     if (cinfo.keyEquivs[i] == (unsigned char)':') {
  730.                                         targetChr = GetHexByte((char *)(cinfo.keyEquivs + i + 1));
  731.                                         targetMod = GetHexByte((char *)(cinfo.keyEquivs + i + 2 + 1)) << 8;
  732.                                         evt.message    = targetChr;
  733.                                         evt.modifiers &= 0x00FF;
  734.                                         evt.modifiers |= (targetMod << 8);
  735.                                         if (cinfo.keyEquivs[0] > i + 4)
  736.                                             if (cinfo.keyEquivs[i + 5] == '<')
  737.                                                 --pass;
  738.                                                     /* If delimiter is a <, then reprocess with pass #0.
  739.                                                     ** This could cause an infinite loop if the targetEquiv
  740.                                                     ** is the same as what was pressed.  The reason that this
  741.                                                     ** is done is if the targetEquiv is different than the
  742.                                                     ** original event, and we want to give non-te, non-list controls
  743.                                                     ** a chance to process the equiv.  Normally the whole purpose for
  744.                                                     ** this feature is to first notice that a pass-0 control has an
  745.                                                     ** equiv, and then we want to pass this key onto a te control. */
  746.                                         i = 999;
  747.                                     }
  748.                                 }
  749.                                 if (i >= 999) continue;
  750.                             }
  751.                         }
  752.  
  753.                         return(Ctl2CNum(*retCtl));
  754.                     }
  755.  
  756.                     break;
  757.                 case 1:
  758.                     if (evt.modifiers & cmdKey) break;        /* Don't allow command keys to go to te and list controls. */
  759.                     if (te = (*gcteFindActive)(window)) {    /* If TextEdit control is active... */
  760.                         if ((*te)->viewRect.left < -8192)
  761.                             BeginFrame(window);
  762.                         else
  763.                             BeginContent(window);
  764.                         *retAction = (*gcteKey)(window, &evt);    /* Allow processing by TE control. */
  765.                         SetOrigin(org.left, org.top);
  766.                         EndContent(window);
  767.                         *retCtl = (*gcteViewFromTE)(te);
  768.                         return(Ctl2CNum(*retCtl));
  769.                     }
  770.                     if (list = (*gclFindActive)(window)) {        /* If List control is active... */
  771.                         if ((*list)->rView.left < -8192)
  772.                             BeginFrame(window);
  773.                         else
  774.                             BeginContent(window);
  775.                         (*gclKey)(window, &evt);
  776.                         SetOrigin(org.left, org.top);
  777.                         EndContent(window);
  778.                         *retCtl = (*gclViewFromList)(list);
  779.                         return(Ctl2CNum(*retCtl));
  780.                     }
  781.                     break;
  782.             }
  783.         }
  784.     }
  785.  
  786.     return(0);
  787. }
  788.  
  789.  
  790.  
  791. /*****************************************************************************/
  792.  
  793.  
  794.  
  795. #pragma segment Controls
  796. static short    HandleScrollEvent(WindowPtr window, EventRecord *event)
  797. {
  798.     WindowPtr        oldPort;
  799.     Point            clickLoc;
  800.     short            part;
  801.     ControlHandle    ctl;
  802.     short            value, h, v;
  803.  
  804.     GetPort(&oldPort);
  805.     SetPort(window);
  806.     gScrollRct = window->portRect;
  807.  
  808.     gKeepOrg.h = gScrollRct.left;
  809.     gKeepOrg.v = gScrollRct.top;
  810.  
  811.     SetOrigin(0, -16384);
  812.     clickLoc = event->where;
  813.     GlobalToLocal(&clickLoc);
  814.         /* Scrollbars for window are offset -16384 vertically.  Get a local
  815.         ** coordinate that corresponds to this negative space. */
  816.  
  817.     if (!(part = FindControl(clickLoc, window, &ctl))) {
  818.         SetOrigin(gKeepOrg.h, gKeepOrg.v);            /* Restore the origin. */
  819.         SetPort(oldPort);
  820.         return(kScrollEvent);
  821.     }
  822.  
  823.     gFrHndl = (FileRecHndl)GetWRefCon(window);
  824.     if ((*gFrHndl)->fileState.vScroll)
  825.         gScrollRct.right  -= 15;
  826.     if ((*gFrHndl)->fileState.hScroll)
  827.         gScrollRct.bottom -= 15;
  828.  
  829.     gScrollRct.left += (*gFrHndl)->fileState.leftSidebar;
  830.     gScrollRct.top  += (*gFrHndl)->fileState.topSidebar;
  831.  
  832.     gVert = (((*ctl)->contrlRect.right - (*ctl)->contrlRect.left) == 16);
  833.     switch (part) {
  834.         case inThumb:
  835.             value = GetCtlValue(ctl);
  836.             SetOrigin(0, -16384);
  837.             part = TrackControl(ctl, clickLoc, nil);
  838.             SetOrigin(gKeepOrg.h, gKeepOrg.v);            /* Restore the origin. */
  839.             if (part) {
  840.                 value -= GetCtlValue(ctl);
  841.                     /* Value now has CHANGE in position.  if position changed, scroll. */
  842.                 if (value) {
  843.                     h = v = 0;
  844.                     if (gVert)
  845.                         v = value;
  846.                     else
  847.                         h = value;
  848.                     ScrollTheContent(window, h, v);
  849.                 }
  850.             }
  851.             break;
  852.         default:
  853.             SetOrigin(0, -16384);
  854.             TrackControl(ctl, clickLoc, (ProcPtr)ScrollActionProc);
  855.             SetOrigin(gKeepOrg.h, gKeepOrg.v);            /* Restore the origin. */
  856.             break;
  857.     }
  858.  
  859.     AdjustScrollBars(window);
  860.  
  861.     SetPort(oldPort);
  862.     return(kScrollEvent);
  863. }
  864.  
  865.  
  866.  
  867. /*****************************************************************************/
  868. /*****************************************************************************/
  869.  
  870.  
  871.  
  872. #pragma segment Controls
  873. static pascal void    ScrollActionProc(ControlHandle scrollCtl, short part)
  874. {
  875.     WindowPtr    window;
  876.     short        delta, value, h, v;
  877.     short        oldValue, max;
  878.     Point        org;
  879.     RgnHandle    contPart, framePart;
  880.  
  881.     GetPort(&window);
  882.  
  883.     if (part) {                        /* If it was actually in the control. */
  884.  
  885.         switch (part) {
  886.             case inUpButton:
  887.             case inDownButton:        /* One line. */
  888.                 delta = (gVert) ? (*gFrHndl)->fileState.vArrowVal : (*gFrHndl)->fileState.hArrowVal;
  889.                 break;
  890.             case inPageUp:            /* One page. */
  891.             case inPageDown:
  892.                 delta = (gVert) ? (*gFrHndl)->fileState.vPageVal : (*gFrHndl)->fileState.hPageVal;
  893.                 break;
  894.         }
  895.  
  896.         if ( (part == inUpButton) || (part == inPageUp) )
  897.             delta = -delta;        /* Reverse direction for an upper. */
  898.  
  899.         value = (oldValue = GetCtlValue(scrollCtl)) + delta;
  900.         if (value < 0)
  901.             value = 0;
  902.         if (value > (max = GetCtlMax(scrollCtl)))
  903.             value = max;
  904.  
  905.         if (value != oldValue) {
  906.  
  907.             SetCtlValue(scrollCtl, value);
  908.             SetOrigin(gKeepOrg.h, gKeepOrg.v);
  909.             h = oldValue - value;
  910.             v = 0;
  911.             if (gVert) {
  912.                 v = h;
  913.                 h = 0;
  914.             }
  915.  
  916.             ScrollTheContent(window, h, v);
  917.  
  918.             DoUpdateSeparate(window, &contPart, &framePart);
  919.             if (contPart) {
  920.                 CopyRgn(contPart, ((WindowPeek)window)->updateRgn);
  921.                 DisposeRgn(contPart);
  922.                 ++gBeginUpdateNested;
  923.                 BeginUpdate(window);
  924.                 GetContentOrigin(window, &org);
  925.                 SetOrigin(org.h, org.v);
  926.                 DoImageDocument(gFrHndl);
  927.                 EndUpdate(window);
  928.                 --gBeginUpdateNested;
  929.             }
  930.             if (framePart) {
  931.                 CopyRgn(framePart, ((WindowPeek)window)->updateRgn);
  932.                 DisposeRgn(framePart);
  933.             }
  934.  
  935.             SetOrigin(0, -16384);
  936.         }
  937.     }
  938. }
  939.  
  940.  
  941.  
  942. /*****************************************************************************/
  943.  
  944.  
  945.  
  946. #pragma segment Controls
  947. static void    ScrollTheContent(WindowPtr window, short h, short v)
  948. {
  949.     Point        org;
  950.     RgnHandle    updateRgn;
  951.  
  952.     org.h = window->portRect.left;
  953.     org.v = window->portRect.top;
  954.     BeginContent(window);
  955.     SetOrigin(org.h, org.v);
  956.     ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
  957.     EndContent(window);
  958.     InvalRgn(updateRgn);
  959.     DisposeRgn(updateRgn);
  960.     DoScrollFrame(window, h, v);
  961. }
  962.  
  963.  
  964.  
  965. /*****************************************************************************/
  966.  
  967.  
  968.  
  969. #pragma segment Controls
  970. static short    GetCtlPosition(ControlHandle ctl)
  971. {
  972.     ControlHandle    nextCtl;
  973.     short            ctlNum;
  974.  
  975.     if (!ctl) return(0);
  976.  
  977.     nextCtl = ((WindowPeek)(*ctl)->contrlOwner)->controlList;
  978.     for (ctlNum = 0;;) {
  979.         if (!nextCtl) break;
  980.         ++ctlNum;
  981.         if (ctl == nextCtl) break;
  982.         nextCtl = (*nextCtl)->nextControl;
  983.     }
  984.     return(ctlNum);
  985. }
  986.  
  987.  
  988.  
  989. /*****************************************************************************/
  990.  
  991.  
  992.  
  993. #pragma segment Controls
  994. ControlHandle    CDataNext(WindowPtr window, ControlHandle ctl)
  995. {
  996.     ControlHandle    tempCtl;
  997.     Rect            rct;
  998.     static Handle    defProc;
  999.  
  1000.     if (!window) return(nil);
  1001.  
  1002.     if (!defProc) {
  1003.         SetRect(&rct, 0, 0, 0, 0);
  1004.         if (tempCtl = NewControl(window, &rct, "\p", false, 0, 0, 0, gDataCtl, 0)) {
  1005.             defProc = (*tempCtl)->contrlDefProc;
  1006.             DisposeControl(tempCtl);
  1007.         }
  1008.     }
  1009.  
  1010.     if (!ctl)
  1011.         ctl = ((WindowPeek)window)->controlList;
  1012.     else
  1013.         ctl = (*ctl)->nextControl;
  1014.  
  1015.     while (ctl) {
  1016.         if (*(*ctl)->contrlDefProc == *defProc) return(ctl);
  1017.             /* The handle may be locked, which means that the hi-bit
  1018.             ** may be on, thus invalidating the compare.  Dereference the
  1019.             ** handles to get rid of this possibility. */
  1020.         ctl = (*ctl)->nextControl;
  1021.     }
  1022.  
  1023.     return(ctl);
  1024. }
  1025.  
  1026.  
  1027.  
  1028.